use std::time::Duration;

use chrono::{ DateTime, NaiveDateTime };
use futures::TryStreamExt;
use serde::{ Deserialize, Serialize };
use serde_json::Value;
use std::sync::Arc;
use tokio::sync::mpsc::Sender;

use anyhow::Result;
use eventsource_client::{ Client, Event };

use crate::REQWEST_CLIENT;

/// Mapping to [STOCK_600519_pankou.json](file:///Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/eastmoney/sample/STOCK_600519_pankou.json)
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct StockPankou {
    pub rc: i32,
    pub rt: i32,
    pub svr: u64,
    pub lt: i32,
    pub full: i32,
    pub dlmkts: String,
    pub data: StockPankouData,
}

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct StockPankouData {
    pub f43: Option<Value>,
    pub f44: Option<Value>,
    pub f45: Option<Value>,
    pub f46: Option<Value>,
    pub f47: Option<Value>,
    pub f48: Option<Value>,
    pub f49: Option<Value>,
    pub f50: Option<Value>,
    pub f51: Option<Value>,
    pub f52: Option<Value>,
    pub f57: Option<Value>,
    pub f58: Option<Value>,
    pub f59: Option<Value>,
    pub f60: Option<Value>,
    pub f71: Option<Value>,
    pub f84: Option<Value>,
    pub f85: Option<Value>,
    pub f86: Option<Value>,
    pub f92: Option<Value>,
    pub f107: Option<Value>,
    pub f108: Option<Value>,
    pub f111: Option<Value>,
    pub f116: Option<Value>,
    pub f117: Option<Value>,
    pub f152: Option<Value>,
    pub f161: Option<Value>,
    pub f162: Option<Value>,
    pub f163: Option<Value>,
    pub f164: Option<Value>,
    pub f167: Option<Value>,
    pub f168: Option<Value>,
    pub f169: Option<Value>,
    pub f170: Option<Value>,
    pub f171: Option<Value>,
    pub f177: Option<Value>,
    pub f191: Option<Value>,
    pub f192: Option<Value>,
    pub f256: Option<Value>,
    pub f257: Option<Value>,
    pub f260: Option<Value>,
    pub f261: Option<Value>,
    pub f262: Option<Value>,
    pub f269: Option<Value>,
    pub f270: Option<Value>,
    pub f277: Option<Value>,
    pub f278: Option<Value>,
    pub f279: Option<Value>,
    pub f285: Option<Value>,
    pub f286: Option<Value>,
    pub f288: Option<Value>,
    pub f292: Option<Value>,
    pub f294: Option<Value>,
    pub f295: Option<Value>,
    pub f301: Option<Value>,
    pub f31: Option<Value>,
    pub f32: Option<Value>,
    pub f33: Option<Value>,
    pub f34: Option<Value>,
    pub f35: Option<Value>,
    pub f36: Option<Value>,
    pub f37: Option<Value>,
    pub f38: Option<Value>,
    pub f39: Option<Value>,
    pub f40: Option<Value>,
    pub f19: Option<Value>,
    pub f20: Option<Value>,
    pub f17: Option<Value>,
    pub f18: Option<Value>,
    pub f15: Option<Value>,
    pub f16: Option<Value>,
    pub f13: Option<Value>,
    pub f14: Option<Value>,
    pub f11: Option<Value>,
    pub f12: Option<Value>,
    pub f734: Option<Value>,
    pub f747: Option<Value>,
    pub f748: Option<Value>,
}

#[derive(Debug)]
pub struct StockPankouEvent {
    pub time: u64,
    pub code: String,
    pub event: Option<Event>,
    pub success: bool,
}

impl StockPankou {
    /// Refer: https://quote.eastmoney.com/sh513130.html
    ///
    /// Data: https://25.push2.eastmoney.com/api/qt/stock/sse?fields=f58,f734,f107,f57,f43,f59,f169,f170,f152,f46,f60,f44,f45,f47,f48,f19,f17,f531,f15,f13,f11,f20,f18,f16,f14,f12,f39,f37,f35,f33,f31,f40,f38,f36,f34,f32,f211,f212,f213,f214,f215,f210,f209,f208,f207,f206,f161,f49,f171,f50,f86,f168,f108,f167,f71,f292,f51,f52,f191,f192,f452,f177&mpi=1000&invt=2&fltt=1&secid=1.513130&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=|0|0|0|web
    ///
    /// secid: str e.g. e.g. sh 1.600519, sz 0.000001/
    pub async fn pankou_sse(
        secid: &str,
        tx: Arc<Sender<StockPankouEvent>>
    ) -> Result<(), Box<dyn std::error::Error>> {
        // GET /api/qt/stock/sse?fields=f58,f734,f107,f57,f43,f59,f169,f170,f152,f46,f60,f44,f45,f47,f48,f19,f17,f531,f15,f13,f11,f20,f18,f16,f14,f12,f39,f37,f35,f33,f31,f40,f38,f36,f34,f32,f211,f212,f213,f214,f215,f210,f209,f208,f207,f206,f161,f49,f171,f50,f86,f168,f108,f167,f71,f292,f51,f52,f191,f192,f452,f177&mpi=1000&invt=2&fltt=1&secid=1.513130&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=|0|0|0|web HTTP/1.1
        // Accept: text/event-stream
        // Accept-Encoding: gzip, deflate, br, zstd
        // Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
        // Cache-Control: no-cache
        // Connection: keep-alive
        // Host: 25.push2.eastmoney.com
        // Origin: https://quote.eastmoney.com
        // Pragma: no-cache
        // Referer: https://quote.eastmoney.com/sh513130.html
        // Sec-Fetch-Dest: empty
        // Sec-Fetch-Mode: cors
        // Sec-Fetch-Site: same-site
        // User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
        // sec-ch-ua: "Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"
        // sec-ch-ua-mobile: ?0
        // sec-ch-ua-platform: "macOS"

        // Query String Parameters:
        //
        // fields: f58,f734,f107,f57,f43,f59,f169,f170,f152,f46,f60,f44,f45,f47,f48,f19,f17,f531,f15,f13,f11,f20,f18,f16,f14,f12,f39,f37,f35,f33,f31,f40,f38,f36,f34,f32,f211,f212,f213,f214,f215,f210,f209,f208,f207,f206,f161,f49,f171,f50,f86,f168,f108,f167,f71,f292,f51,f52,f191,f192,f452,f177
        // mpi: 1000
        // invt: 2
        // fltt: 1
        // secid: 1.513130
        // ut: fa5fd1943c7b386f172d6893dbfba10b
        // wbp2u: |0|0|0|web

        // {"rc":0,"rt":4,"svr":182992899,"lt":2,"full":0,"dlmkts":"8,10,128","data":{"f43":481,"f86":1710297127,"f169":0,"f170":0,"f191":313,"f192":119978,"f452":-1262,"f32":186914,"f206":0,"f34":249743,"f207":702,"f36":272259,"f208":-2788,"f38":662564,"f209":-760,"f40":486894,"f210":12297,"f20":755059,"f211":-33956,"f18":358995,"f212":4592,"f16":336076,"f213":0,"f47":23540282,"f48":1144223975.0,"f50":203,"f71":486,"f161":11717208,"f168":703,"f49":11823074,"f14":312196,"f214":-100}}
        let host = "75.push2delay.eastmoney.com";
        let fields =
            "f58,f734,f107,f57,f43,f59,f169,f170,f152,f46,f60,f44,f45,f47,f48,f19,f17,f531,f15,f13,f11,f20,f18,f16,f14,f12,f39,f37,f35,f33,f31,f40,f38,f36,f34,f32,f211,f212,f213,f214,f215,f210,f209,f208,f207,f206,f161,f49,f171,f50,f86,f168,f108,f167,f71,f292,f51,f52,f191,f192,f452,f177";
        let mpi = 1000;
        let invt = 2;
        let fltt = 1;
        let ut = utils::generate_random_hex_string(32);
        let wbp2u = "|0|0|0|web";

        // Create the URL for the request, we need to copy the sse request information exactly from [http://quote.eastmoney.com/sh513130.html] secid=1.513130 secid=0.000001
        let url = format!(
            "https://{host}/api/qt/stock/sse?fields={fields}&mpi={mpi}&invt={invt}&fltt={fltt}&secid={secid}&ut={ut}&wbp2u={wbp2u}"
        );

        // let url = "http://75.push2delay.eastmoney.com/api/qt/stock/sse?fields=f58,f734,f107,f57,f43,f59,f169,f170,f152,f46,f60,f44,f45,f47,f48,f19,f17,f531,f15,f13,f11,f20,f18,f16,f14,f12,f39,f37,f35,f33,f31,f40,f38,f36,f34,f32,f211,f212,f213,f214,f215,f210,f209,f208,f207,f206,f161,f49,f171,f50,f86,f168,f108,f167,f71,f292,f51,f52,f191,f192,f452,f177&mpi=1000&invt=2&fltt=1&secid=0.000001&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=|0|0|0|web";
        // let url = "http://75.push2delay.eastmoney.com/api/qt/stock/sse?secid=1.601127&ut=fa5fd1943c7b386f172d6893dbfba10b"; // test ok: simplified url
        // let url = "http://75.push2delay.eastmoney.com/api/qt/stock/sse?mpi=1000&invt=2&fltt=1&secid=0.000001&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=|0|0|0|web";
        println!("\nData: {}", url);

        let eventsource_client = eventsource_client::ClientBuilder
            ::for_url(&url)?
            .header(reqwest::header::ACCEPT.as_str(), "text/event-stream")?
            .header("Accept-Encoding", "gzip, deflate, br")?
            .header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")?
            .header("Cache-Control", "no-cache")?
            .header("Connection", "keep-alive")?
            .header("Host", &format!("{host}"))
            ? // must be same as url's host
            .header("Origin", "http://quote.eastmoney.com")?
            .header("Referer", "http://quote.eastmoney.com/sh513130.html")?
            .header("Sec-Fetch-Dest", "empty")?
            .header("Sec-Fetch-Mode", "cors")?
            .header("Sec-Fetch-Site", "cross-site")?
            .header(
                "User-Agent",
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
            )?
            .header(
                "sec-ch-ua",
                "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\""
            )?
            .header("sec-ch-ua-mobile", "?0")?
            .reconnect(
                eventsource_client::ReconnectOptions
                    ::reconnect(true)
                    .retry_initial(false)
                    .delay(Duration::from_secs(1))
                    .backoff_factor(2)
                    .delay_max(Duration::from_secs(60))
                    .build()
            )
            .build();

        let mut eventsource_stream = eventsource_client
            .stream()
            .map_ok(|event| {
                match event {
                    eventsource_client::SSE::Event(ev) => {
                        // println!("\n[{secid}] got an event: {} {}", ev.event_type, ev.data);
                        Some(ev)
                    }
                    eventsource_client::SSE::Comment(_comment) => {
                        // println!("\n[{secid}] got a comment: \n{}", comment);
                        None
                    }
                }
            })
            .map_err(|err| eprintln!("\n[{secid}] error streaming events: {:?}", err));

        while let Ok(Some(Some(evt))) = eventsource_stream.try_next().await {
            let evt_: StockPankouEvent = StockPankouEvent {
                time: utils::duration_ms_since_epoch(),
                code: secid.to_owned(),
                event: Some(evt.clone()),
                success: true,
            };

            println!(
                "\n>>> {:?} {} {}-{}",
                DateTime::from_timestamp_millis(evt_.time.try_into().unwrap()),
                evt_.code,
                evt.event_type,
                evt.data
            );

            let _ = tx.send(evt_).await;
        }

        println!("\n\n****pankou_sse for {secid} End caused by error!!!???\n");
        let evt_: StockPankouEvent = StockPankouEvent {
            time: utils::duration_ms_since_epoch(),
            code: secid.to_owned(),
            event: None,
            success: false,
        };
        let _ = tx.send(evt_).await;

        Ok(())
    }

    /// Refer:  https://quote.eastmoney.com/sh513130.html
    ///
    /// Data: https://push2delay.eastmoney.com/api/qt/stock/get?invt=2&fltt=1&&fields=f58%2Cf734%2Cf107%2Cf57%2Cf43%2Cf59%2Cf169%2Cf170%2Cf152%2Cf46%2Cf60%2Cf44%2Cf45%2Cf47%2Cf48%2Cf19%2Cf17%2Cf531%2Cf15%2Cf13%2Cf11%2Cf20%2Cf18%2Cf16%2Cf14%2Cf12%2Cf39%2Cf37%2Cf35%2Cf33%2Cf31%2Cf40%2Cf38%2Cf36%2Cf34%2Cf32%2Cf211%2Cf212%2Cf213%2Cf214%2Cf215%2Cf210%2Cf209%2Cf208%2Cf207%2Cf206%2Cf161%2Cf49%2Cf171%2Cf50%2Cf86%2Cf168%2Cf108%2Cf167%2Cf71%2Cf292%2Cf51%2Cf52%2Cf191%2Cf192%2Cf452%2Cf177&secid=1.513130&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=%7C0%7C0%7C0%7Cweb&_=1710297338887
    ///
    /// secid: e.g. 1.513130, 0.000001
    ///
    pub async fn pankou_get(secid: &str) -> Result<StockPankou> {
        // GET /api/qt/stock/get?invt=2&fltt=1&cb=jQuery35105854069358511618_1710297338886&fields=f58%2Cf734%2Cf107%2Cf57%2Cf43%2Cf59%2Cf169%2Cf170%2Cf152%2Cf46%2Cf60%2Cf44%2Cf45%2Cf47%2Cf48%2Cf19%2Cf17%2Cf531%2Cf15%2Cf13%2Cf11%2Cf20%2Cf18%2Cf16%2Cf14%2Cf12%2Cf39%2Cf37%2Cf35%2Cf33%2Cf31%2Cf40%2Cf38%2Cf36%2Cf34%2Cf32%2Cf211%2Cf212%2Cf213%2Cf214%2Cf215%2Cf210%2Cf209%2Cf208%2Cf207%2Cf206%2Cf161%2Cf49%2Cf171%2Cf50%2Cf86%2Cf168%2Cf108%2Cf167%2Cf71%2Cf292%2Cf51%2Cf52%2Cf191%2Cf192%2Cf452%2Cf177&secid=1.513130&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=%7C0%7C0%7C0%7Cweb&_=1710297338887 HTTP/1.1
        // Accept: */*
        // Accept-Encoding: gzip, deflate, br, zstd
        // Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
        // Cache-Control: no-cache
        // Connection: keep-alive
        // Cookie: qgqp_b_id=d6299473145338f68687834ba37fa719; st_si=77687174285423; AUTH_FUND.EASTMONEY.COM_GSJZ=AUTH*TTJJ*TOKEN; EMFUND0=01-29%2015%3A02%3A07@%23%24%u534E%u590F%u4E2D%u8BC1%u5168%u6307%u623F%u5730%u4EA7ETF@%23%24515060; EMFUND1=01-30%2014%3A41%3A22@%23%24%u534E%u6CF0%u67CF%u745E%u6D88%u8D39%u6210%u957F%u6DF7%u5408@%23%24001069; EMFUND2=01-30%2014%3A45%3A59@%23%24%u5E7F%u53D1%u6052%u751F%u6D88%u8D39%28QDII-ETF%29@%23%24159699; EMFUND3=01-30%2014%3A47%3A24@%23%24%u5357%u65B9%u7EA2%u5229%u4F4E%u6CE250ETF@%23%24515450; EMFUND4=01-31%2015%3A21%3A23@%23%24%u56FD%u8054%u4E2D%u8BC1%u7164%u70AD%u6307%u6570%28LOF%29A@%23%24168204; EMFUND5=02-02%2008%3A34%3A56@%23%24%u666F%u987A%u957F%u57CE%u652F%u67F1%u4EA7%u4E1A%u6DF7%u5408@%23%24260117; EMFUND6=02-02%2010%3A30%3A22@%23%24%u897F%u90E8%u5229%u5F97%u805A%u79BE%u6DF7%u5408A@%23%24007423; EMFUND7=02-26%2016%3A07%3A01@%23%24%u91D1%u9E70%u6DFB%u76C8%u7EAF%u503A%u503A%u5238A@%23%24003384; EMFUND8=02-27%2010%3A51%3A37@%23%24%u534E%u590F%u6210%u957F%u6DF7%u5408@%23%24000001; EMFUND9=02-27 13:53:07@#$%u534E%u6CF0%u67CF%u745E%u5357%u65B9%u4E1C%u82F1%u6052%u751F%u79D1%u6280%28QDII-ETF%29@%23%24513130; st_asi=delete; st_pvi=47260449691882; st_sp=2024-01-12%2017%3A28%3A27; st_inirUrl=https%3A%2F%2Fwww.google.com.hk%2F; st_sn=1929; st_psi=20240313103035987-113200301201-1445190704; HAList=ty-1-513130-%u6052%u751F%u79D1%u6280ETF%2Cty-0-300078-%u601D%u521B%u533B%u60E0%2Cty-1-600519-%u8D35%u5DDE%u8305%u53F0%2Cty-0-300059-%u4E1C%u65B9%u8D22%u5BCC%2Cty-0-837046-%u4EBF%u80FD%u7535%u529B%2Cty-0-000001-%u5E73%u5B89%u94F6%u884C%2Cty-1-601360-%u4E09%u516D%u96F6%2Cty-1-600055-%u4E07%u4E1C%u533B%u7597%2Cty-0-159001-%u8D27%u5E01ETF%2Cty-0-161127-%u6807%u666E%u751F%u7269%u79D1%u6280LOF
        // Host: push2delay.eastmoney.com
        // Pragma: no-cache
        // Referer: https://quote.eastmoney.com/sh513130.html
        // Sec-Fetch-Dest: script
        // Sec-Fetch-Mode: no-cors
        // Sec-Fetch-Site: same-site
        // User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
        // sec-ch-ua: "Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"
        // sec-ch-ua-mobile: ?0
        // sec-ch-ua-platform: "macOS"

        // Query String Parameters
        //
        // invt: 2
        // fltt: 1
        // cb: jQuery35105854069358511618_1710297338886 //可以不要
        // fields: f58,f734,f107,f57,f43,f59,f169,f170,f152,f46,f60,f44,f45,f47,f48,f19,f17,f531,f15,f13,f11,f20,f18,f16,f14,f12,f39,f37,f35,f33,f31,f40,f38,f36,f34,f32,f211,f212,f213,f214,f215,f210,f209,f208,f207,f206,f161,f49,f171,f50,f86,f168,f108,f167,f71,f292,f51,f52,f191,f192,f452,f177
        // secid: 1.513130
        // ut: fa5fd1943c7b386f172d6893dbfba10b
        // wbp2u: |0|0|0|web
        // _: 1710297338887

        let ut = utils::generate_random_hex_string(32);
        let t_ = utils::duration_ms_since_epoch();

        let request_url = format!(
            "https://push2delay.eastmoney.com/api/qt/stock/get?invt=2&fltt=1&&fields=f58%2Cf734%2Cf107%2Cf57%2Cf43%2Cf59%2Cf169%2Cf170%2Cf152%2Cf46%2Cf60%2Cf44%2Cf45%2Cf47%2Cf48%2Cf19%2Cf17%2Cf531%2Cf15%2Cf13%2Cf11%2Cf20%2Cf18%2Cf16%2Cf14%2Cf12%2Cf39%2Cf37%2Cf35%2Cf33%2Cf31%2Cf40%2Cf38%2Cf36%2Cf34%2Cf32%2Cf211%2Cf212%2Cf213%2Cf214%2Cf215%2Cf210%2Cf209%2Cf208%2Cf207%2Cf206%2Cf161%2Cf49%2Cf171%2Cf50%2Cf86%2Cf168%2Cf108%2Cf167%2Cf71%2Cf292%2Cf51%2Cf52%2Cf191%2Cf192%2Cf452%2Cf177&secid={secid}&ut={ut}&wbp2u=%7C0%7C0%7C0%7Cweb&_={t_}"
        );

        let response = REQWEST_CLIENT.get(request_url.clone())
            .header(reqwest::header::ACCEPT, "*/*")
            .header(reqwest::header::ACCEPT_ENCODING, "gzip, deflate, br")
            .header(reqwest::header::ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8")
            .header(reqwest::header::CACHE_CONTROL, "no-cache")
            .header(reqwest::header::CONNECTION, "keep-alive")
            // .header(reqwest::header::COOKIE, "qgqp_b_id=d6299473145338f68687834ba37fa719; st_si=77687174285423; AUTH_FUND.EASTMONEY.COM_GSJZ=AUTH*TTJJ*TOKEN; EMFUND0=01-16%2016%3A57%3A06@%23%24%u4E2D%u822A%u65B0%u8D77%u822A%u7075%u6D3B%u914D%u7F6E%u6DF7%u5408A@%23%24005537; EMFUND1=01-16%2017%3A04%3A53@%23%24%u91D1%u4FE1%u6D88%u8D39%u5347%u7EA7%u80A1%u7968A@%23%24006692; EMFUND2=01-16%2020%3A01%3A56@%23%24%u5357%u65B9%u4E0A%u8BC1380ETF@%23%24510290; EMFUND3=01-29%2015%3A02%3A07@%23%24%u534E%u590F%u4E2D%u8BC1%u5168%u6307%u623F%u5730%u4EA7ETF@%23%24515060; EMFUND4=01-30%2014%3A41%3A22@%23%24%u534E%u6CF0%u67CF%u745E%u6D88%u8D39%u6210%u957F%u6DF7%u5408@%23%24001069; EMFUND5=01-30%2014%3A45%3A59@%23%24%u5E7F%u53D1%u6052%u751F%u6D88%u8D39%28QDII-ETF%29@%23%24159699; EMFUND6=01-30%2014%3A47%3A24@%23%24%u5357%u65B9%u7EA2%u5229%u4F4E%u6CE250ETF@%23%24515450; EMFUND7=01-31%2015%3A21%3A23@%23%24%u56FD%u8054%u4E2D%u8BC1%u7164%u70AD%u6307%u6570%28LOF%29A@%23%24168204; EMFUND8=02-02%2008%3A34%3A56@%23%24%u666F%u987A%u957F%u57CE%u652F%u67F1%u4EA7%u4E1A%u6DF7%u5408@%23%24260117; EMFUND9=02-02 10:30:22@#$%u897F%u90E8%u5229%u5F97%u805A%u79BE%u6DF7%u5408A@%23%24007423; st_asi=delete; HAList=ty-1-513130-%u6052%u751F%u79D1%u6280ETF%2Cty-0-300078-%u601D%u521B%u533B%u60E0%2Cty-0-300486-%u4E1C%u6770%u667A%u80FD%2Cty-1-513360-%u6559%u80B2ETF%2Cty-0-300478-%u676D%u5DDE%u9AD8%u65B0%2Cty-0-301589-N%u8BFA%u74E6%2Cty-1-603135-%u4E2D%u91CD%u79D1%u6280%2Cty-0-000001-%u5E73%u5B89%u94F6%u884C%2Cty-0-002400-%u7701%u5E7F%u96C6%u56E2%2Cty-1-601127-%u8D5B%u529B%u65AF; st_pvi=47260449691882; st_sp=2024-01-12%2017%3A28%3A27; st_inirUrl=https%3A%2F%2Fwww.google.com.hk%2F; st_sn=438; st_psi=20240226102321941-113200301321-9024721593")
            .header(reqwest::header::HOST, "push2delay.eastmoney.com")
            .header(reqwest::header::PRAGMA, "no-cache")
            .header(reqwest::header::REFERER, "https://quote.eastmoney.com/sh513130.html")
            .header(
                reqwest::header::USER_AGENT,
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
            )
            .header(
                "Sec-Ch-Ua",
                "\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\""
            )
            .header("Sec-Ch-Ua-Mobile", "?0")
            .header("Sec-Ch-Ua-Platform", "\"macOS\"")
            .header("Sec-Fetch-Dest", "empty")
            .header("Sec-Fetch-Mode", "cors")
            .header("Sec-Fetch-Site", "same-origin")
            .send().await?;

        let body = response.text().await?;

        // println!("*** body={}\n\nhangqin\n\n", body);

        let fund: StockPankou = serde_json::from_str(&body)?;

        // println!("{:?}", fund);

        println!("\n Data: {}\n", request_url);
        Ok(fund)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use regex::Regex;
    use std::sync::Arc;
    use tokio::sync::mpsc::{ channel, Sender };

    #[tokio::test]
    async fn test_pankou_sse() {
        let symbols = vec!["1.600519", "1.513130", "1.511670", "1.515880", "0.159509", "1.515050"]
            // let symbols = vec!["0.159509","1.515050"]
            .iter()
            .map(|&symbol| symbol.to_string())
            .collect::<Vec<String>>();

        let (tx, mut rx) = channel::<StockPankouEvent>(100); // Create a channel
        let tx_shared = Arc::new(tx);

        let mut handles = vec![];

        for symbol in &symbols {
            let tx_clone = Arc::clone(&tx_shared);
            let symbol_clone = symbol.clone();
            let handle = tokio::spawn(async move {
                process_symbol(symbol_clone, tx_clone).await;
            });
            handles.push(handle);
        }

        // Process events sequentially from the shared receiver
        while let Some(event) = rx.recv().await {
            let _dd: StockPankou = serde_json
                ::from_str(&event.event.unwrap().data)
                .unwrap_or_default();
            // println!("<<< Received event: {} {} {:?}", event.time, event.code,dd );

            if event.success == false {
                break;
            }
        }

        for handle in handles {
            handle.await.unwrap(); // Handle errors if needed
        }

        println!("End of all handles");
    }

    async fn process_symbol(symbol: String, tx: Arc<Sender<StockPankouEvent>>) {
        // Use the shared sender to send events
        let _ = StockPankou::pankou_sse(&symbol, tx).await;
    }

    fn remove_before_dot(s: &str) -> String {
        let re = Regex::new(r"^(.*?)\.").unwrap();
        re.replace_all(s, "").to_string()
    }
    #[tokio::test]
    async fn test_pankou_get() {
        let symbols = vec!["1.600519", "1.513130", "1.511670", "1.515880", "0.159509", "1.515050"]
            // let symbols = vec!["0.159509","1.515050"]
            .iter()
            .map(|&symbol| symbol.to_string())
            .collect::<Vec<String>>();

        for symbol in &symbols {
            let result = StockPankou::pankou_get(symbol).await;

            assert_eq!(result.is_ok(), true);
            if let Ok(stock_pankou) = result {
                assert_eq!(stock_pankou.data.f57, Some(Value::String(remove_before_dot(symbol))));
                println!("stock_pankou={:?}\n", stock_pankou);
            }
        }
    }
}
